package main

import (
	"fmt"
	"sync"
)

/*
不推荐共享变量的方式通信
锁---》解决共享资源的竞争
mutex----》互斥锁  atomic—---》 atomic存在一些原子操作方法，比如加减
Mutex（互斥锁）是并发编程中用于保护共享资源不被多个goroutine同时修改的一种同步机制。
Mutex是互斥锁的意思，也叫排他锁，同一时刻一段代码只能被一个线程运行，使用只需要关注方法Lock（加锁）和Unlock（解锁）即可。
在Lock()和Unlock()之间的代码段称为资源的临界区(critical section)，是线程安全的，
任何一个时间点都只能有一个goroutine执行这段区间的代码。在Go中，sync.Mutex提供了基本的互斥功能，确保在任意时刻只有一个goroutine可以访问临界区。
锁可以复制吗，锁复制之后会失去锁的效果---》不能复制
*/

var wg sync.WaitGroup // 声明初始化WaitGroup
var lock sync.Mutex   // 声明初始化互斥锁
var total = 0         //初始化全局共享变量，共享资源
var total2 int32 = 0  //初始化全局共享变量，共享资源
func add1() {
	//关闭协监视器
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock() //添加锁，测试资源竞争这里可以注释
		// atomic.AddInt32(&total2, 1) // atomic存在一些原子操作方法，比如加减，这样就不需加锁，也能防止资源竞争
		total += i    // 操作共享资源，当有其他的操作的时候会有竞争
		lock.Unlock() //解锁，测试资源竞争这里可以注释
	}
}

func del1() {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		lock.Lock() //添加锁，测试资源竞争这里可以注释
		// atomic.AddInt32(&total2, -1)  // atomic存在一些原子操作方法，比如加减，这样就不需加锁，也能防止资源竞争
		total -= i    // 操作共享资源，当有其他的操作的时候会有竞争
		lock.Unlock() //解锁，测试资源竞争这里可以注释
	}
}

/*
由于上面两个函数有三个步骤，加载a 计算a 写入a的值，由于这三个步骤都不是原子操作，两goroutine在执行的时候不是某个先执行全部步骤在执行第二个
goroutine，所以导致会存在竞争的，导致最后结果不是0，结果存在不确定性，所以需要加入互斥锁，当自己在操作资源的是时候其他的协程不能操作，
加入互斥锁，最后结果才是0
atomic存在一些原子操作方法，比如加减，这样就不需加锁，也能防止资源竞争，但只能是某些方法，而互斥锁能为某些代码逻辑加锁更加灵活
*/

func main() {
	//增加协程监视器
	wg.Add(2)
	// 指向goroutine
	go add1()
	go del1()
	//等待协程结束
	wg.Wait()
	fmt.Println("total:", total)
	fmt.Println("total2:", total2)
}
